package cn.tf.tfpay.fap.client;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.Charset;
import java.util.List;
import java.util.function.Consumer;

import org.apache.tomcat.util.codec.binary.Base64;
import org.apache.tomcat.util.http.fileupload.IOUtils;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.core.io.InputStreamResource;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.http.client.SimpleClientHttpRequestFactory;
import org.springframework.web.client.RestTemplate;

import cn.tf.tfpay.fap.server.FapUtils;
import cn.tf.tfpay.fap.server.RemoteFile;

/**
 * 基于HTTP短链接的的NFS客户端实现.
 * 
 * @author cgnb_liuxb
 */
public class HttpFapClient implements FapClient {
	/** HTTP代理服务器地址 */
	private String baseUrl;
	private int connectTimeout = 5000;
	private int readTimeout = 10000;

	RestTemplate restTemplate;

	public HttpFapClient(String baseUrl, int connectTimeout, int readTimeout) {
		this.baseUrl = baseUrl;
		this.connectTimeout = connectTimeout;
		this.readTimeout = readTimeout;

		SimpleClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory();
		requestFactory.setConnectTimeout(this.connectTimeout);
		requestFactory.setReadTimeout(this.readTimeout);
		requestFactory.setBufferRequestBody(false);
		restTemplate = new RestTemplate(requestFactory);
	}

	@Override
	public RemoteFile get(String path) {
		return restTemplate.getForObject(baseUrl + "/get/" + FapUtils.encodePath(path), RemoteFile.class);
	}

	@Override
	public byte[] read(RemoteFile remoteFile) {
		String base64Str = restTemplate.getForObject(baseUrl + "/readBase64/" + remoteFile.getBase64Path(),
				String.class);
		return Base64.decodeBase64(base64Str);
	}

	@Override
	public String readString(RemoteFile remoteFile, Charset charset) {
		byte[] data = read(remoteFile);
		return new String(data, charset);
	}

	@Override
	public void readStream(RemoteFile remoteFile, Consumer<InputStream> consumer) {
		restTemplate.execute(baseUrl + "/readStream/" + remoteFile.getBase64Path(), HttpMethod.GET, null,
				clientHttpResponse -> {
					consumer.accept(clientHttpResponse.getBody());
					return null;
				});
	}

	@Override
	public void download(RemoteFile remoteFile, File localFile) {
		this.readStream(remoteFile, is->{
			try (FileOutputStream fos = new FileOutputStream(localFile)) {
				IOUtils.copy(is, fos);
			}catch (IOException e) {
				throw new RuntimeException(e);
			}
		});
	}

	@Override
	public List<RemoteFile> list(RemoteFile dir) {
		return restTemplate.exchange(baseUrl + "/list/" + dir.getBase64Path(), HttpMethod.GET, null,
				new ParameterizedTypeReference<List<RemoteFile>>() {
				}).getBody();
	}

	@Override
	public void write(RemoteFile remoteFile, byte[] data) {
		if (data.length > 100 * 1024 * 1024) {
			throw new IllegalArgumentException("数据太大,请使用writeStream!");
		}
		restTemplate.postForObject(baseUrl + "/writeBase64/" + remoteFile.getBase64Path(),
				Base64.encodeBase64(data, true), Void.class);
	}

	@Override
	public void writeStream(RemoteFile remoteFile, InputStream is) {
		HttpEntity<InputStreamResource> requestEntity = new HttpEntity<>(new InputStreamResource(is));
		ResponseEntity<Long> res = restTemplate.exchange(baseUrl + "/writeStream/" + remoteFile.getBase64Path(),
				HttpMethod.POST, requestEntity, Long.class);
		System.out.println("response:" + res);
	}

	@Override
	public boolean deleteFile(RemoteFile remoteFile) {
		return restTemplate.getForObject(baseUrl + "/deleteFile/" + remoteFile.getBase64Path(), Boolean.class);
	}

	@Override
	public boolean makeDir(RemoteFile remoteFile) {
		return restTemplate.getForObject(baseUrl + "/makeDir/" + remoteFile.getBase64Path(), Boolean.class);
	}

	@Override
	public boolean deleteDir(RemoteFile remoteFile) {
		return restTemplate.getForObject(baseUrl + "/deleteDir/" + remoteFile.getBase64Path(), Boolean.class);
	}

}
